"""
闲鱼匿名请求服务
使用匿名请求获取商品和卖家信息，避免风控
"""

import json
from typing import Dict, Any, Optional
from utils.log_util import logger
from utils.anonymous_goofish_client import AnonymousGoofishClient


class GoofishAnonymousService:
    """闲鱼匿名请求服务类"""
    
    # Redis缓存键前缀
    CACHE_KEY_TOKEN_POOL = "goofish:anonymous:token_pool"  # Token池（Hash）
    CACHE_KEY_TOKEN_USAGE = "goofish:anonymous:token_usage:{token}"  # Token使用计数
    CACHE_KEY_ITEM = "goofish:anonymous:item:{item_id}"
    CACHE_KEY_SELLER = "goofish:anonymous:seller:{seller_id}"
    
    # Token缓存时间：30分钟（1800秒）- 缩短避免失效
    TOKEN_CACHE_TTL = 1800
    # Token使用计数TTL：10分钟（600秒）
    TOKEN_USAGE_TTL = 600
    # Token池大小：最多保留多少个token
    TOKEN_POOL_SIZE = 10
    # Token最大使用次数（超过后丢弃）- 降低避免token过期
    TOKEN_MAX_USAGE = 15
    
    # 商品详情缓存时间：1小时（3600秒）
    ITEM_CACHE_TTL = 3600
    
    @classmethod
    async def get_token_from_pool(cls, redis) -> Optional[str]:
        """
        从token池获取可用token（负载均衡）
        
        Args:
            redis: Redis客户端
        
        Returns:
            token 字符串，如果失败返回 None
        """
        try:
            # 1. 从token池获取所有token
            token_pool = await redis.hgetall(cls.CACHE_KEY_TOKEN_POOL)
            
            if token_pool:
                # 2. 批量获取所有token的使用次数（优化：减少Redis往返次数）
                usage_keys = []
                token_map = {}  # {token: token_key}
                
                for token_key, token_value in token_pool.items():
                    token = token_value.decode() if isinstance(token_value, bytes) else token_value
                    usage_key = cls.CACHE_KEY_TOKEN_USAGE.format(token=token)
                    usage_keys.append(usage_key)
                    token_map[token] = token_key
                
                # 批量获取使用计数（1次Redis调用，而不是N次）
                usage_counts = await redis.mget(usage_keys) if usage_keys else []
                
                # 3. 选择使用次数最少的token
                min_usage = float('inf')
                selected_token = None
                tokens_to_remove = []
                
                for i, (token, token_key) in enumerate(token_map.items()):
                    usage = int(usage_counts[i]) if usage_counts[i] else 0
                    
                    # 如果使用次数超过最大值，标记为待移除
                    if usage >= cls.TOKEN_MAX_USAGE:
                        tokens_to_remove.append(token_key)
                        logger.info(f"Token使用次数超限，将从池中移除: {token[:20]}...")
                        continue
                    
                    if usage < min_usage:
                        min_usage = usage
                        selected_token = token
                
                # 批量移除超限token
                if tokens_to_remove:
                    await redis.hdel(cls.CACHE_KEY_TOKEN_POOL, *tokens_to_remove)
                
                if selected_token:
                    # 增加使用计数
                    usage_key = cls.CACHE_KEY_TOKEN_USAGE.format(token=selected_token)
                    await redis.incr(usage_key)
                    await redis.expire(usage_key, cls.TOKEN_USAGE_TTL)
                    logger.debug(f"从token池选择token（使用次数={min_usage + 1}）: {selected_token[:20]}...")
                    return selected_token
            
            # 3. 池中没有可用token，创建新token
            logger.debug("Token池为空，创建新token...")
            async with AnonymousGoofishClient() as client:
                new_token = await client.get_token()
                if new_token:
                    # 添加到token池
                    import time
                    token_key = f"token_{int(time.time() * 1000)}"
                    await redis.hset(cls.CACHE_KEY_TOKEN_POOL, token_key, new_token)
                    # 设置token池的过期时间
                    await redis.expire(cls.CACHE_KEY_TOKEN_POOL, cls.TOKEN_CACHE_TTL)
                    # 初始化使用计数
                    usage_key = cls.CACHE_KEY_TOKEN_USAGE.format(token=new_token)
                    await redis.setex(usage_key, cls.TOKEN_USAGE_TTL, 1)
                    logger.info(f"新token已添加到池: {new_token[:20]}...")
                    
                    # 限制token池大小
                    pool_size = await redis.hlen(cls.CACHE_KEY_TOKEN_POOL)
                    if pool_size > cls.TOKEN_POOL_SIZE:
                        # 移除最老的token
                        oldest_key = await redis.hkeys(cls.CACHE_KEY_TOKEN_POOL)
                        if oldest_key:
                            await redis.hdel(cls.CACHE_KEY_TOKEN_POOL, oldest_key[0])
                            logger.debug(f"Token池已满，移除最老的token")
                    
                    return new_token
                else:
                    logger.error("创建新token失败")
                    return None
        except Exception as e:
            logger.exception(f"从token池获取token时出错: {e}")
            return None
    
    @classmethod
    async def get_token_from_cache_or_fetch(cls, redis) -> Optional[str]:
        """
        从token池获取token（兼容旧接口）
        
        Args:
            redis: Redis客户端
        
        Returns:
            token 字符串，如果失败返回 None
        """
        return await cls.get_token_from_pool(redis)
    
    @classmethod
    async def get_item_detail(cls, redis, item_id: int, use_cache: bool = True) -> Optional[Dict[str, Any]]:
        """
        获取商品详情（带缓存）
        
        Args:
            redis: Redis客户端
            item_id: 商品ID
            use_cache: 是否使用缓存（默认True）
        
        Returns:
            商品详情数据，如果失败返回 None
        """
        cache_key = cls.CACHE_KEY_ITEM.format(item_id=item_id)
        
        try:
            # 尝试从缓存获取
            if use_cache:
                cached_data = await redis.get(cache_key)
                if cached_data:
                    logger.debug(f"从缓存获取商品详情: itemId={item_id}")
                    return json.loads(cached_data)
            
            # 缓存中没有，重新获取
            logger.debug(f"缓存中没有商品详情，重新获取: itemId={item_id}")
            
            # ✅ 从token池获取token（负载均衡，避免单个token过载）
            async with AnonymousGoofishClient() as client:
                # 从token池获取可用token
                pool_token = await cls.get_token_from_pool(redis)
                if pool_token:
                    client.token = pool_token
                    logger.debug(f"使用token池中的token: {pool_token[:20]}...")
                else:
                    logger.error("无法从token池获取token，无法请求商品详情")
                    return None
                
                # 获取商品详情（token应该在cookie中，或通过client.token属性传递）
                detail = await client.get_item_detail(item_id)
                
                if detail:
                    # 保存到缓存，有效期1小时
                    await redis.setex(cache_key, cls.ITEM_CACHE_TTL, json.dumps(detail, ensure_ascii=False))
                    logger.debug(f"商品详情已保存到缓存，有效期{cls.ITEM_CACHE_TTL}秒")
                    return detail
                else:
                    logger.error(f"获取商品详情失败: itemId={item_id}")
                    return None
        except Exception as e:
            logger.exception(f"获取商品详情时出错: {e}")
            return None
    
    @classmethod
    async def get_item_and_seller_info(cls, redis, item_id: int, use_cache: bool = True) -> Optional[Dict[str, Any]]:
        """
        获取商品和卖家信息（简化版本，用于聊天窗口）
        
        Args:
            redis: Redis客户端
            item_id: 商品ID
            use_cache: 是否使用缓存（默认True）
        
        Returns:
            包含商品和卖家信息的字典，格式：
            {
                'item_title': str,
                'item_price': str,
                'item_image': str,
                'item_tags': list,
                'seller_id': int,
                'seller_name': str,
                'seller_avatar': str
            }
        """
        try:
            detail = await cls.get_item_detail(redis, item_id, use_cache=use_cache)
            if not detail:
                return None
            
            item_do = detail.get("itemDO", {})
            seller_do = detail.get("sellerDO", {})
            
            # 提取商品信息
            item_title = item_do.get("title", "")
            item_price = item_do.get("soldPrice") or item_do.get("price", "")
            
            # 获取商品图片（第一张）
            image_infos = item_do.get("imageInfos", [])
            item_image = image_infos[0].get("url", "") if image_infos else ""
            
            # 提取商品标签（去重）
            item_tags = []
            tag_set = set()  # 使用set去重
            
            # 价格相关标签
            price_tags = item_do.get("priceRelativeTags", [])
            for tag in price_tags:
                tag_text = tag.get("text", "").strip()
                if tag_text and tag_text not in tag_set:
                    tag_set.add(tag_text)
                    item_tags.append(tag_text)
            
            # 通用标签
            common_tags = item_do.get("commonTags", [])
            for tag in common_tags:
                tag_text = tag.get("text", "").strip()
                if tag_text and tag_text not in tag_set:
                    tag_set.add(tag_text)
                    item_tags.append(tag_text)
            
            # 信用标签
            zhima_level = seller_do.get("zhimaLevelInfo", {})
            if zhima_level:
                level_name = zhima_level.get("levelName", "").strip()
                if level_name and level_name not in tag_set:
                    tag_set.add(level_name)
                    item_tags.append(level_name)
            
            # 提取卖家信息
            seller_id = seller_do.get("sellerId")
            seller_name = seller_do.get("nick", "")
            seller_avatar = seller_do.get("portraitUrl", "")
            
            # ✅ 提取商品状态（0=在线, 1=已卖掉）
            item_status = item_do.get("itemStatus")
            item_status_str = item_do.get("itemStatusStr", "")
            
            result = {
                'item_title': item_title,
                'item_price': item_price,
                'item_image': item_image,
                'item_tags': item_tags,
                'seller_id': seller_id,
                'seller_name': seller_name,
                'seller_avatar': seller_avatar,
                # ✅ 添加完整的图片和视频信息（用于媒体查看器）
                'image_infos': item_do.get("imageInfos", []),
                'video_play_info': item_do.get("videoPlayInfo")
            }
            
            # ✅ 如果商品状态存在，添加到返回结果中
            if item_status is not None:
                result['item_status'] = item_status
                result['item_status_str'] = item_status_str
            
            return result
        except Exception as e:
            logger.exception(f"提取商品和卖家信息时出错: {e}")
            return None

